package service

import (
	"crypto/md5"
	"encoding/hex"
	"errors"
	"fmt"
	"image"
	_ "image/gif"
	_ "image/jpeg"
	_ "image/png"
	"io"
	"mime/multipart"
	"os"
	"path/filepath"
	"strings"
	"time"

	"fitness/go-admin/internal/model"
	"fitness/go-admin/internal/repository"

	"github.com/disintegration/imaging"
	"gorm.io/gorm"
)

// FileService 文件服务接口
type FileService interface {
	// 文件上传
	UploadFile(fileHeader *multipart.FileHeader, req *model.UploadFileReq) (*model.UploadFileResp, error)

	// 文件管理
	GetFileByID(id uint) (*model.FileRecord, error)
	GetFileList(req *model.FileListReq) (*model.FileListResp, error)
	DeleteFile(id uint, operatorID uint) error
	BatchDeleteFiles(ids []uint, operatorID uint) error
	GetFileStatistics(uploaderID uint, uploaderType string) (*model.FileStatistics, error)

	// 训练照片
	AddTrainingPhoto(req *model.AddTrainingPhotoReq) error
	GetTrainingPhoto(id uint) (*model.TrainingPhoto, error)
	GetTrainingPhotoList(req *model.TrainingPhotoListReq) (*model.TrainingPhotoListResp, error)
	DeleteTrainingPhoto(id uint, memberID uint) error
	GetMemberPhotoTimeline(memberID uint) ([]model.TrainingPhoto, error)
	TogglePhotoPublic(id uint, memberID uint, isPublic bool) error

	// 教练证书
	AddCoachCertificate(req *model.AddCertificateReq) error
	GetCoachCertificate(id uint) (*model.CoachCertificate, error)
	GetCoachCertificateList(req *model.CertificateListReq) (*model.CertificateListResp, error)
	DeleteCoachCertificate(id uint, coachID uint) error
	VerifyCertificate(id uint, req *model.VerifyCertificateReq, operatorID uint) error
	GetCoachCertificates(coachID uint) ([]model.CoachCertificate, error)
}

// fileService 文件服务实现
type fileService struct {
	repo       repository.FileRepository
	db         *gorm.DB
	uploadPath string // 上传文件存储路径
	baseURL    string // 文件访问基础URL
}

// NewFileService 创建文件服务实例
func NewFileService(repo repository.FileRepository, db *gorm.DB) FileService {
	// 配置上传路径和基础URL
	uploadPath := "./uploads" // 实际项目中应从配置文件读取
	baseURL := "/uploads"     // 实际项目中应从配置文件读取

	// 确保上传目录存在
	os.MkdirAll(uploadPath, 0755)
	os.MkdirAll(filepath.Join(uploadPath, "avatar"), 0755)
	os.MkdirAll(filepath.Join(uploadPath, "certificate"), 0755)
	os.MkdirAll(filepath.Join(uploadPath, "training"), 0755)
	os.MkdirAll(filepath.Join(uploadPath, "thumbnail"), 0755)

	return &fileService{
		repo:       repo,
		db:         db,
		uploadPath: uploadPath,
		baseURL:    baseURL,
	}
}

// ======================== 文件上传 ========================

// 文件上传限制配置
const (
	MaxFileSize     = 5 * 1024 * 1024 // 5MB
	MaxAvatarSize   = 2 * 1024 * 1024 // 2MB
	ThumbnailWidth  = 200             // 缩略图宽度
	ThumbnailHeight = 200             // 缩略图高度
	AvatarMaxWidth  = 800             // 头像最大宽度
	AvatarMaxHeight = 800             // 头像最大高度
)

// 允许的文件类型
var allowedImageTypes = map[string]bool{
	"image/jpeg": true,
	"image/jpg":  true,
	"image/png":  true,
	"image/gif":  true,
}

var allowedCertTypes = map[string]bool{
	"image/jpeg":      true,
	"image/jpg":       true,
	"image/png":       true,
	"application/pdf": true,
}

// UploadFile 上传文件
func (s *fileService) UploadFile(fileHeader *multipart.FileHeader, req *model.UploadFileReq) (*model.UploadFileResp, error) {
	// 1. 验证文件
	if err := s.validateFile(fileHeader, req.FileType); err != nil {
		return nil, err
	}

	// 2. 打开上传的文件
	file, err := fileHeader.Open()
	if err != nil {
		return nil, errors.New("打开文件失败")
	}
	defer file.Close()

	// 3. 计算文件MD5（秒传功能）
	fileMD5, err := s.calculateMD5(file)
	if err != nil {
		return nil, errors.New("计算文件MD5失败")
	}

	// 检查文件是否已存在
	existingFile, _ := s.repo.GetFileRecordByMD5(fileMD5)
	if existingFile != nil {
		// 文件已存在，秒传
		return &model.UploadFileResp{
			FileID:       existingFile.ID,
			FileName:     existingFile.FileName,
			FileURL:      existingFile.FileURL,
			ThumbnailURL: existingFile.ThumbnailURL,
			FileSize:     existingFile.FileSize,
			FileType:     string(existingFile.FileType),
			UploadedAt:   existingFile.CreatedAt.Format("2006-01-02 15:04:05"),
		}, nil
	}

	// 4. 重置文件指针
	file.Seek(0, 0)

	// 5. 生成文件名和路径
	fileName := s.generateFileName(fileHeader.Filename)
	fileDir := filepath.Join(s.uploadPath, string(req.FileType))
	filePath := filepath.Join(fileDir, fileName)

	// 6. 保存文件
	if err := s.saveFile(file, filePath); err != nil {
		return nil, err
	}

	// 7. 处理图片（压缩、生成缩略图）
	var thumbnailURL string
	var width, height int
	mimeType := fileHeader.Header.Get("Content-Type")

	if s.isImage(mimeType) {
		width, height, err = s.getImageDimensions(filePath)
		if err == nil {
			// 头像需要压缩
			if req.FileType == model.FileTypeAvatar {
				s.compressAvatar(filePath, width, height)
			}
			// 生成缩略图
			thumbnailPath := s.generateThumbnail(filePath, fileName)
			if thumbnailPath != "" {
				thumbnailURL = strings.Replace(thumbnailPath, s.uploadPath, s.baseURL, 1)
			}
		}
	}

	// 8. 创建文件记录
	fileURL := strings.Replace(filePath, s.uploadPath, s.baseURL, 1)
	fileURL = strings.ReplaceAll(fileURL, "\\", "/") // Windows路径处理

	fileRecord := &model.FileRecord{
		FileName:     fileHeader.Filename,
		FileType:     req.FileType,
		FilePath:     filePath,
		FileURL:      fileURL,
		ThumbnailURL: thumbnailURL,
		FileSize:     fileHeader.Size,
		MimeType:     mimeType,
		MD5:          fileMD5,
		Width:        width,
		Height:       height,
		StorageType:  model.StorageTypeLocal,
		UploaderID:   req.UploaderID,
		UploaderType: req.UploaderType,
		Status:       model.FileStatusNormal,
	}

	if err := s.repo.CreateFileRecord(fileRecord); err != nil {
		// 创建记录失败，删除已上传的文件
		os.Remove(filePath)
		if thumbnailURL != "" {
			os.Remove(strings.Replace(thumbnailURL, s.baseURL, s.uploadPath, 1))
		}
		return nil, errors.New("创建文件记录失败")
	}

	// 9. 返回响应
	return &model.UploadFileResp{
		FileID:       fileRecord.ID,
		FileName:     fileRecord.FileName,
		FileURL:      fileRecord.FileURL,
		ThumbnailURL: fileRecord.ThumbnailURL,
		FileSize:     fileRecord.FileSize,
		FileType:     string(fileRecord.FileType),
		UploadedAt:   fileRecord.CreatedAt.Format("2006-01-02 15:04:05"),
	}, nil
}

// ======================== 文件管理 ========================

// GetFileByID 获取文件信息
func (s *fileService) GetFileByID(id uint) (*model.FileRecord, error) {
	return s.repo.GetFileRecordByID(id)
}

// GetFileList 获取文件列表
func (s *fileService) GetFileList(req *model.FileListReq) (*model.FileListResp, error) {
	files, total, err := s.repo.GetFileRecordList(req)
	if err != nil {
		return nil, err
	}

	return &model.FileListResp{
		Total: int(total),
		Page:  req.Page,
		Size:  req.PageSize,
		List:  files,
	}, nil
}

// DeleteFile 删除文件
func (s *fileService) DeleteFile(id uint, operatorID uint) error {
	// 获取文件记录
	_, err := s.repo.GetFileRecordByID(id)
	if err != nil {
		return errors.New("文件不存在")
	}

	// 权限检查（简化版，实际项目中需要更复杂的权限验证）
	// 这里假设只有上传者本人或管理员可以删除
	// if file.UploaderID != operatorID {
	// 	return errors.New("无权删除此文件")
	// }

	// 软删除文件记录
	if err := s.repo.DeleteFileRecord(id); err != nil {
		return err
	}

	// 可选：删除物理文件（根据业务需求决定是否真正删除）
	// os.Remove(file.FilePath)
	// if file.ThumbnailURL != "" {
	// 	thumbnailPath := strings.Replace(file.ThumbnailURL, s.baseURL, s.uploadPath, 1)
	// 	os.Remove(thumbnailPath)
	// }

	return nil
}

// BatchDeleteFiles 批量删除文件
func (s *fileService) BatchDeleteFiles(ids []uint, operatorID uint) error {
	return s.repo.BatchDeleteFiles(ids)
}

// GetFileStatistics 获取文件统计
func (s *fileService) GetFileStatistics(uploaderID uint, uploaderType string) (*model.FileStatistics, error) {
	return s.repo.GetFileStatistics(uploaderID, uploaderType)
}

// ======================== 训练照片 ========================

// AddTrainingPhoto 添加训练照片
func (s *fileService) AddTrainingPhoto(req *model.AddTrainingPhotoReq) error {
	// 验证文件是否存在
	file, err := s.repo.GetFileRecordByID(req.FileID)
	if err != nil {
		return errors.New("文件不存在")
	}

	// 验证文件类型
	if file.FileType != model.FileTypeTraining {
		return errors.New("文件类型不正确")
	}

	// 解析训练日期
	trainingDate, err := time.Parse("2006-01-02", req.TrainingDate)
	if err != nil {
		return errors.New("训练日期格式错误")
	}

	// 创建训练照片记录
	photo := &model.TrainingPhoto{
		MemberID:     req.MemberID,
		FileID:       req.FileID,
		PhotoType:    req.PhotoType,
		TrainingDate: trainingDate,
		Weight:       req.Weight,
		BodyFat:      req.BodyFat,
		Description:  req.Description,
		IsPublic:     req.IsPublic,
	}

	return s.repo.CreateTrainingPhoto(photo)
}

// GetTrainingPhoto 获取训练照片
func (s *fileService) GetTrainingPhoto(id uint) (*model.TrainingPhoto, error) {
	return s.repo.GetTrainingPhotoByID(id)
}

// GetTrainingPhotoList 获取训练照片列表
func (s *fileService) GetTrainingPhotoList(req *model.TrainingPhotoListReq) (*model.TrainingPhotoListResp, error) {
	photos, total, err := s.repo.GetTrainingPhotoList(req)
	if err != nil {
		return nil, err
	}

	return &model.TrainingPhotoListResp{
		Total: int(total),
		Page:  req.Page,
		Size:  req.PageSize,
		List:  photos,
	}, nil
}

// DeleteTrainingPhoto 删除训练照片
func (s *fileService) DeleteTrainingPhoto(id uint, memberID uint) error {
	// 获取照片
	photo, err := s.repo.GetTrainingPhotoByID(id)
	if err != nil {
		return errors.New("照片不存在")
	}

	// 权限检查
	if photo.MemberID != memberID {
		return errors.New("无权删除此照片")
	}

	return s.repo.DeleteTrainingPhoto(id)
}

// GetMemberPhotoTimeline 获取会员照片时间线
func (s *fileService) GetMemberPhotoTimeline(memberID uint) ([]model.TrainingPhoto, error) {
	return s.repo.GetMemberPhotoTimeline(memberID)
}

// TogglePhotoPublic 切换照片公开状态
func (s *fileService) TogglePhotoPublic(id uint, memberID uint, isPublic bool) error {
	photo, err := s.repo.GetTrainingPhotoByID(id)
	if err != nil {
		return errors.New("照片不存在")
	}

	if photo.MemberID != memberID {
		return errors.New("无权修改此照片")
	}

	photo.IsPublic = isPublic
	return s.repo.UpdateTrainingPhoto(photo)
}

// ======================== 教练证书 ========================

// AddCoachCertificate 添加教练证书
func (s *fileService) AddCoachCertificate(req *model.AddCertificateReq) error {
	// 验证文件是否存在
	file, err := s.repo.GetFileRecordByID(req.FileID)
	if err != nil {
		return errors.New("文件不存在")
	}

	// 验证文件类型
	if file.FileType != model.FileTypeCertificate {
		return errors.New("文件类型不正确")
	}

	// 创建证书记录
	cert := &model.CoachCertificate{
		CoachID:         req.CoachID,
		FileID:          req.FileID,
		CertificateName: req.CertificateName,
		CertificateNo:   req.CertificateNo,
		IssuingOrg:      req.IssuingOrg,
		IsVerified:      false,
	}

	// 解析日期
	if req.IssueDate != "" {
		issueDate, err := time.Parse("2006-01-02", req.IssueDate)
		if err == nil {
			cert.IssueDate = issueDate
		}
	}
	if req.ExpiryDate != "" {
		expiryDate, err := time.Parse("2006-01-02", req.ExpiryDate)
		if err == nil {
			cert.ExpiryDate = expiryDate
		}
	}
	if req.Remark != "" {
		cert.Remark = req.Remark
	}

	return s.repo.CreateCoachCertificate(cert)
}

// GetCoachCertificate 获取教练证书
func (s *fileService) GetCoachCertificate(id uint) (*model.CoachCertificate, error) {
	return s.repo.GetCoachCertificateByID(id)
}

// GetCoachCertificateList 获取教练证书列表
func (s *fileService) GetCoachCertificateList(req *model.CertificateListReq) (*model.CertificateListResp, error) {
	certs, total, err := s.repo.GetCoachCertificateList(req)
	if err != nil {
		return nil, err
	}

	return &model.CertificateListResp{
		Total: int(total),
		Page:  req.Page,
		Size:  req.PageSize,
		List:  certs,
	}, nil
}

// DeleteCoachCertificate 删除教练证书
func (s *fileService) DeleteCoachCertificate(id uint, coachID uint) error {
	// 获取证书
	cert, err := s.repo.GetCoachCertificateByID(id)
	if err != nil {
		return errors.New("证书不存在")
	}

	// 权限检查
	if cert.CoachID != coachID {
		return errors.New("无权删除此证书")
	}

	return s.repo.DeleteCoachCertificate(id)
}

// VerifyCertificate 验证证书
func (s *fileService) VerifyCertificate(id uint, req *model.VerifyCertificateReq, operatorID uint) error {
	return s.repo.VerifyCoachCertificate(id, req.IsVerified, operatorID, req.Remark)
}

// GetCoachCertificates 获取教练的所有证书
func (s *fileService) GetCoachCertificates(coachID uint) ([]model.CoachCertificate, error) {
	return s.repo.GetCoachCertificates(coachID)
}

// ======================== 私有方法 ========================

// validateFile 验证文件
func (s *fileService) validateFile(fileHeader *multipart.FileHeader, fileType model.FileType) error {
	// 验证文件大小
	maxSize := int64(MaxFileSize)
	if fileType == model.FileTypeAvatar {
		maxSize = MaxAvatarSize
	}
	if fileHeader.Size > maxSize {
		return fmt.Errorf("文件大小超过限制（最大 %dMB）", maxSize/1024/1024)
	}

	// 验证文件类型
	mimeType := fileHeader.Header.Get("Content-Type")
	var allowed map[string]bool
	if fileType == model.FileTypeCertificate {
		allowed = allowedCertTypes
	} else {
		allowed = allowedImageTypes
	}

	if !allowed[mimeType] {
		return errors.New("不支持的文件类型")
	}

	return nil
}

// calculateMD5 计算文件MD5
func (s *fileService) calculateMD5(file multipart.File) (string, error) {
	hash := md5.New()
	if _, err := io.Copy(hash, file); err != nil {
		return "", err
	}
	return hex.EncodeToString(hash.Sum(nil)), nil
}

// generateFileName 生成文件名
func (s *fileService) generateFileName(originalName string) string {
	ext := filepath.Ext(originalName)
	timestamp := time.Now().Format("20060102150405")
	return fmt.Sprintf("%s_%d%s", timestamp, time.Now().UnixNano()%1000000, ext)
}

// saveFile 保存文件
func (s *fileService) saveFile(src multipart.File, dst string) error {
	out, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer out.Close()

	_, err = io.Copy(out, src)
	return err
}

// isImage 判断是否为图片
func (s *fileService) isImage(mimeType string) bool {
	return allowedImageTypes[mimeType]
}

// getImageDimensions 获取图片尺寸
func (s *fileService) getImageDimensions(filePath string) (int, int, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return 0, 0, err
	}
	defer file.Close()

	img, _, err := image.DecodeConfig(file)
	if err != nil {
		return 0, 0, err
	}

	return img.Width, img.Height, nil
}

// compressAvatar 压缩头像
func (s *fileService) compressAvatar(filePath string, width, height int) error {
	// 如果尺寸已经合适，不需要压缩
	if width <= AvatarMaxWidth && height <= AvatarMaxHeight {
		return nil
	}

	src, err := imaging.Open(filePath)
	if err != nil {
		return err
	}

	// 等比例缩放
	dst := imaging.Fit(src, AvatarMaxWidth, AvatarMaxHeight, imaging.Lanczos)

	// 保存
	return imaging.Save(dst, filePath)
}

// generateThumbnail 生成缩略图
func (s *fileService) generateThumbnail(filePath, fileName string) string {
	src, err := imaging.Open(filePath)
	if err != nil {
		return ""
	}

	// 生成缩略图
	thumbnail := imaging.Fill(src, ThumbnailWidth, ThumbnailHeight, imaging.Center, imaging.Lanczos)

	// 保存缩略图
	thumbnailDir := filepath.Join(s.uploadPath, "thumbnail")
	thumbnailPath := filepath.Join(thumbnailDir, "thumb_"+fileName)

	if err := imaging.Save(thumbnail, thumbnailPath); err != nil {
		return ""
	}

	return thumbnailPath
}
